<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
      .hello {
          font-size: 18px;
      }

      .red {
          color: maroon;
      }
  </style>
</head>
<body>
<div contenteditable id="elem" class="hello">Click and <b>edit</b>, please</div>
<button id="btn">改变class</button>
<button id="btn2">改变contentEditable</button>
<script>
  /**
   * MutationObserver 可以对 DOM 的变化作出反应 —— 特性（attribute），文本内容，添加/删除元素。
   * 我们可以用它来跟踪代码其他部分引入的更改，以及与第三方脚本集成。
   * MutationObserver 可以跟踪任何更改。config “要观察的内容”选项用于优化，避免不必要的回调调用以节省资源。
   * @type {MutationObserver}
   */
  const observer = new MutationObserver((mutationRecords) => {
    console.log('mutationRecords:', mutationRecords)
    // MutationRecord 对象具有以下属性：

    // type —— 变动类型，以下类型之一：
    //    "attributes"：特性被修改了，
    //    "characterData"：数据被修改了，用于文本节点，
    //    "childList"：添加/删除了子元素。
    // target —— 更改发生在何处："attributes" 所在的元素，或 "characterData" 所在的文本节点，或 "childList" 变动所在的元素，
    // addedNodes/removedNodes —— 添加/删除的节点，
    // previousSibling/nextSibling —— 添加/删除的节点的上一个/下一个兄弟节点，
    // attributeName/attributeNamespace —— 被更改的特性的名称/命名空间（用于 XML），
    // oldValue —— 之前的值，仅适用于特性或文本更改，如果设置了相应选项 attributeOldValue/characterDataOldValue。
  })
  observer.observe(elem, {
    childList: true, // 监听直属子节点的更改
    characterData: true, // 监听文本节点内容更改
    characterDataOldValue: true, // 是否将老值传回回调
    subtree: true, // 监听所有后代节点的发生的更改（childList、characterData、attributes），设置false只监听直属子节点的更改
    attributes: true, // 监听elem的特性
    attributeOldValue: true, // 是否将老值传回回调
    attributeFilter: ['class'] // 特性名称数组，只观察选定的特性。 这里只观察了class 属性的变化
  })

  /**
   * 其他方法
   * 有一个方法可以停止观察节点：
   * observer.disconnect() —— 停止观察。
   * 当我们停止观察时，观察器可能尚未处理某些更改。在种情况下，我们使用：
   * observer.takeRecords() —— 获取尚未处理的变动记录列表。
   * 此方法最常见的使用场景是在断开观察者之前立即获取所有未处理的更改记录，以便在停止观察者时可以处理任何未处理的更改
   */

  btn.onclick = () => {
    if (elem.classList.contains('red')) {
      elem.classList.remove('red')
      return
    }
    elem.classList.add('red')
  }
  // 点击改变contentEditable
  btn2.onclick = () => {
    if (elem.getAttribute('contentEditable') === 'true') {
      elem.setAttribute('contentEditable', false)
      return
    }
    elem.setAttribute('contentEditable', true)
  }
</script>
</body>
</html>
